/*
 * ref_count.c
 *
 *  Created on: 2015年9月29日
 *      Author: chens
 */
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include <cm/struct/ref_count.h>

struct _data {
    void * ptr;
    int count;
};

struct ref_count_t {
    int total;
    pthread_mutex_t lock;
    struct _data * pdata;
};

cm_ref_t * cmref_new(int capacity)
{
    cm_ref_t * pret = NULL;
    pret = (cm_ref_t *) malloc(sizeof(struct ref_count_t));
    if (pret == NULL)
        return NULL;

    pret->pdata = (struct _data *) calloc(capacity, sizeof(struct _data));
    if (pret->pdata == NULL) {
        free(pret);
        return NULL;
    }
    pthread_mutex_init(&(pret->lock), NULL);
    pret->total = capacity;

    return pret;
}

void cmref_del(cm_ref_t * p)
{
    if (p == NULL)
        return;

    pthread_mutex_destroy(&(p->lock));
    if (p->pdata != NULL)
        free(p->pdata);

    free(p);
}

int cmref_dec(cm_ref_t * p, void * ptr)
{
    int i;
    int ret = 0;
    if (0 != pthread_mutex_lock(&(p->lock)))
        return -1;

    for (i = 0; i < p->total; i++) {
        if (p->pdata[i].ptr == ptr) {
            ret = --(p->pdata[i].count);
            if (ret <= 0)
                p->pdata[i].ptr = NULL;
            break;
        }
    }

    pthread_mutex_unlock(&(p->lock));

    return ret;
}

/*
 * @brief   递增指针 ptr 的引用计数
 *
 * @return  成功返回当前引用计数 > 0, 失败返回-1
 */
int cmref_add(cm_ref_t * p, void * ptr)
{
    int i;
    struct _data *ptemp = NULL;
    if (0 != pthread_mutex_lock(&(p->lock)))
        return -1;

    for (i = 0; i < p->total; i++) {
        if (p->pdata[i].ptr == ptr) {
            p->pdata[i].count++;
            break;
        } else {
            if (ptemp == NULL && p->pdata[i].ptr == NULL) {
                ptemp = &(p->pdata[i]);
            }
        }
    }

    if (i == p->total) { // 没有找到则添加
        if (ptemp != NULL) {
            ptemp->ptr = ptr;
            ptemp->count = 1;
        } else {
            printf("ref_count_add failed.\n"); // error
        }
    }

    pthread_mutex_unlock(&(p->lock));

    return 0;
}

/*
 * @brief   直接设置 ptr 的引用计数
 */
int cmref_set(cm_ref_t * p, void * ptr, int count)
{
    int i;
    struct _data *ptemp = NULL;

    if(count <= 0)
        return 0;

    if (0 != pthread_mutex_lock(&(p->lock)))
        return -1;

    for (i = 0; i < p->total; i++) {
        if (p->pdata[i].ptr == ptr) {
            p->pdata[i].count = count;
            break;
        } else {
            if (ptemp == NULL && p->pdata[i].ptr == NULL) {
                ptemp = &(p->pdata[i]);
            }
        }
    }

    if (i == p->total) { // 没有找到则添加
        if (ptemp != NULL) {
            ptemp->ptr = ptr;
            ptemp->count = count;
        } else {
            printf("ref_count_add failed.\n"); // error
        }
    }

    pthread_mutex_unlock(&(p->lock));
    return 0;
}

/*
 * @brief   获取指针 ptr 的引用计数
 *
 * @return  成功返回0
 */
int cmref_get(cm_ref_t * p, void * ptr)
{
    int i;
    int ret = -1;
    if (0 != pthread_mutex_lock(&(p->lock)))
        return -1;

    for (i = 0; i < p->total; i++) {
        if (p->pdata[i].ptr == ptr) {
            ret = p->pdata[i].count;
            break;
        }
    }

    pthread_mutex_unlock(&(p->lock));

    return ret;
}

